package xmpp.client.api.Group;

import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import xmpp.client.api.User.UserNotFoundException;
import xmpp.client.api.User.UsersXMPP;
import xmpp.client.api.bd.BD;
import xmpp.client.api.bd.XMPPExceptionSQL;

/**
 * Clase para manejar los grupos de XMPP (listar, agregar, quitar...)
 * 
 * @author Miguel Gonzalez - Juanan Mangudo - Telecor
 */
public class GroupsXMPP  {
    //Constantes publicas
    public static final String SHOWINROSTER_EVERYBODY = "everybody";
    public static final String SHOWINROSTER_NOBODY = "nobody";
    public static final String SHOWINROSTER_ONLYGROUP = "onlyGroup";
    
    /*
     * Consultas de selección
     */
    //ofGroup
    private static final String SELECT_GROUP_COUNT = "SELECT count(*) FROM ofGroup";
    
    private static final String SELECT_GROUP_NAME_NAMES = "SELECT groupName FROM ofGroup";
    
    //ofGroupUser
    private static final String SELECT_GROUP_USER_GROUPNAME = "SELECT groupName FROM ofGroupUser"
            + " WHERE username=?";
    
    private static final String SELECT_GROUP_DESCRIPTION = "SELECT description FROM ofGroup"
            + " WHERE groupName=?";
    
    private static final String SELECT_GROUP_USER_USERNAMES = "SELECT username FROM ofGroupUser"
            + " WHERE groupName=? AND administrator=?";
    
    private static final String SELECT_GROUP_PROPERTY_GROUPSHARED = "SELECT groupName FROM ofGroupProp"
            + " WHERE name='sharedRoster.showInRoster' " +
        "AND propValue IS NOT NULL AND propValue <> 'nobody'";
    
    private static final String SELECT_GROUP_PROPERTY = "SELECT propValue FROM ofGroupProp"
            + " WHERE groupName=? AND name=?";
    
    /*
     * Consultas de actualización
     */
    
    //ofGroup
    private static final String UPADTE_GROUP_NAME = "UPDATE `ofGroup` SET `groupName`=?"
            + " WHERE `groupName`=?";
    
    //ofGroupUser
    private static final String UPDATE_GROUP_USER_NAME = "UPDATE `ofGroupUser` SET `groupName`=?"
            + " WHERE `groupName`=?";
    
    private static final String UPDATE_GROUP_MEMBER_ADMINISTRATOR = "UPDATE `ofGroupUser` SET"
            + " `administrator`=? WHERE `groupName`=? AND `username`=?";
    
    //ofGroupProp
    private static final String UPDATE_GROUP_DESCRIPTION = "UPDATE `ofGroup` SET `description`=?"
            + " WHERE `groupName`=?";
    
    private static final String UPDATE_GROUP_PROPERTY_NAME = "UPDATE `ofGroupProp` SET"
            + " `groupName`=? WHERE `groupName`=?";
    
    private static final String UPDATE_GROUP_PROPERTY_VALUE = "UPDATE `ofGroupProp` SET `propValue`=?"
            + " WHERE `groupName`=? AND `name`=?";
    
    
    
    /*
     * Consultas de inserción
     */
    
    //ofGroupUser
    private static final String INSERT_GROUP_USER = "INSERT INTO `ofGroupUser`(`groupName`,"
            + " `username`, `administrator`) VALUES (?,?,?)";
    
    //ofGroup
    private static final String INSERT_GROUP = "INSERT INTO `ofGroup`(`groupName`,"
            + " `description`) VALUES (?,?)";
    
    //ofGroupProp
    private static final String INSERT_GROUP_PROPERTY = "INSERT INTO `ofGroupProp`(`groupName`,"
            + " `name`, `propValue`) VALUES (?,?,?)";
    
    /**
     * Consultas de borrado
     */
    
    //ofGroupUser
    private static final String DELETE_GROUP_USER = "DELETE FROM `ofGroupUser`"
            + " WHERE `groupName`=? AND `username`=?";
    
    //Borrados
    private static final String DELETE_GROUP = "DELETE FROM `ofGroup` WHERE `groupName`=?";
    
    private static final String DELETE_GROUP_PROPERTY_ALLPROPERTYS = "DELETE FROM `ofGroupProp`"
            + " WHERE `groupName`=?";
    
    private static final String DELETE_GROUP_USER_ALLUSERS = "DELETE FROM `ofGroupUser`"
            + " WHERE `groupName`=?";
    
    //Conexiones
    private BD bd;
    private Connection connection = null;
    
    /**
     * Constructor de la clase de manejo de grupos de OpenFire
     * 
     * @throws XMPPExceptionSQL 
     */
    public GroupsXMPP() throws XMPPExceptionSQL {
        bd = new BD();
    }
    
    /**
     * Abre una conexión contra la base de datos
     * @param userName Usuario de la base de datos
     * @param passwordName Contraseña de la base de datos
     * @param host Host de la base de datos
     * @param bdName Nombre de la base de datos
     * @throws XMPPExceptionSQL 
     */
    public void openConnection(String userName, String passwordName, String host, String bdName) throws XMPPExceptionSQL {
        bd.openConnection(userName, passwordName, host, bdName);
        connection = bd.getConection();
    }
    
    /**
     * Establece una conexión
     * @param connection Recibe una conexión
     */
    public void setConnection(Connection connection) {
        this.connection = connection;
    }
    
    /**
     * Cierra la conexión contra base de datos
     * @throws XMPPExceptionSQL Lanza una excepción XMPPExceptionSQL
     */
    public void closeConnection() throws XMPPExceptionSQL {
        try {
            connection.close();
            bd.closeConnection();
        } catch (SQLException ex) {
            throw new XMPPExceptionSQL(ex);
        }
    }
    
    /**
     * Añade un grupo a OpenFire
     * @param grupo Grupo de OpenFire que se quiere agregar
     * @throws XMPPExceptionSQL Si hay alguna excepción sql al insertar el grupo se lanza
     * @throws GroupCreateException Si hay algun problema al crear el grupo se lanza
     * @throws GroupAlreadyExistException Si el grupo ya existe se lanza la excepción
     * @throws UserNotFoundException Si el usuario no se encuentra (lista de usuarios
     * del grupo) se lanza la excepción
     */
    public void addGroup(GroupXMPP grupo) throws XMPPExceptionSQL, GroupCreateException,
            GroupAlreadyExistException , UserNotFoundException {
        if(existGroup(grupo.getName())) {
            throw new GroupAlreadyExistException("El grupo ya existe");
        }
        
        try {
            PreparedStatement pstmt = null;
            ResultSet rs = null;
            try {
                //Nombre y descripción del grupo
                pstmt = (PreparedStatement)connection.prepareStatement(INSERT_GROUP);
                pstmt.setString(1, grupo.getName());
                pstmt.setString(2, grupo.getDescription());

                int updated = pstmt.executeUpdate();

                if(updated == 0) {
                    throw new GroupCreateException("Hubo un problema al insertar el grupo");
                }

                //Propiedad de showInRoster
                pstmt = (PreparedStatement)connection.prepareStatement(INSERT_GROUP_PROPERTY);
                pstmt.setString(1, grupo.getName());
                pstmt.setString(2, "sharedRoster.showInRoster");
                pstmt.setString(3, grupo.getShowInRoster());

                updated = pstmt.executeUpdate();

                if(updated == 0) {
                    throw new GroupCreateException("Hubo un problema al insertar el grupo");
                }

                //Propiedad de displayName
                pstmt.setString(2, "sharedRoster.displayName");
                pstmt.setString(3, grupo.getDisplayName());

                updated = pstmt.executeUpdate();

                if(updated == 0) {
                    throw new GroupCreateException("Hubo un problema al insertar el grupo");
                }

                //Propiedad de groupList
                ArrayList<String> groupList = grupo.groupList();
                String groupListString = "";
                for(int i=0; i<groupList.size(); i++) {
                    //Si es el último no añadimos la coma al final
                    if(i == groupList.size() - 1) {
                        groupListString = groupListString + groupList.get(i);
                    } else {
                        groupListString = groupListString + groupList.get(i) + ",";
                    }
                }
                pstmt.setString(2, "sharedRoster.groupList");
                pstmt.setString(3, groupListString);

                pstmt.executeUpdate();

                if(updated == 0) {
                    throw new GroupCreateException("Hubo un problema al insertar el grupo");
                }

                //Agregamos los usuarios y administradores
                ArrayList<String> members = grupo.getMembers();
                for(String member : members) {
                    addGroupMember(grupo.getName(), member, false);
                }

                ArrayList<String> admins = grupo.getAdministrators();
                for(String admin : admins) {
                    addGroupMember(grupo.getName(), admin, true);
                }            
            }
            catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }
            finally {
                try {

                    if(pstmt != null) {
                        pstmt.close();
                    }

                    if(rs != null) {
                        rs.close();
                    }

                } catch (SQLException sqle) {
                    throw new XMPPExceptionSQL(sqle);
                }            
            }
        } catch(GroupNotFoundException e) {
            throw new GroupCreateException(e);
        }
    }
    
    /**
     * Añade un usuario existente a un grupo de OpenFire
     * @param groupName Nombre del grupo donde se va a agregar el usuario
     * @param userName Nombre del usuario que se va a agregar
     * @param isAdministrator Indica si el usuario es administrador del grupo
     * @throws XMPPExceptionSQL Si hay alguna excepción sql al insertar el usuario se lanza
     * @throws GroupNotFoundException Si el grupo no existe se lanza la excepción
     * @throws UserNotFoundException Si el usuario no existe se lanza la excepción
     */
    public void addGroupMember(String groupName, String userName, boolean isAdministrator)
            throws XMPPExceptionSQL, GroupNotFoundException, UserNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        UsersXMPP usersXMPP = new UsersXMPP();
        usersXMPP.setConnection(connection);
        
        if(usersXMPP.userExists(userName)) {
            throw new UserNotFoundException("El usuario no existe");
        }
        
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(INSERT_GROUP_USER);
            pstmt.setString(1, groupName);
            pstmt.setString(2, userName);
            if(isAdministrator) {
                pstmt.setString(3, "1");
            } else {
                pstmt.setString(3, "0");
            }
            
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
    
    /**
     * Comprueba si existe un grupo en openfire
     * @param groupName Nombre del grupo
     * @return Devuelve true si existe el grupo, sino falso
     * @throws XMPPExceptionSQL Lanza una excepción XMPPExceptionSQL
     */
    public boolean existGroup(String groupName) throws XMPPExceptionSQL {
        ArrayList<String> groupNames = getGroupNames();
        boolean exist = false;
        for(String name : groupNames) {
            if(name.toLowerCase().equals(groupName.toLowerCase())) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * Borra un grupo de OpenFire
     * @param groupName Nombre del grupo de OpenFire
     * @throws XMPPExceptionSQL Si hay algún error SQL se lanza la excepción
     * @throws GroupCreateException Si hay algún problema al borrar el grupo se lanza la excepción
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza la excepción
     */
    public void deleteGroup(String groupName) throws XMPPExceptionSQL, GroupCreateException, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Borramos los datos del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(DELETE_GROUP);
            pstmt.setString(1, groupName);
            
            int updated = pstmt.executeUpdate();
            
            if(updated == 0) {
                throw new GroupCreateException("Hubo un problema al borrar el grupo");
            }
            
            //Borramos las propiedades del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(DELETE_GROUP_PROPERTY_ALLPROPERTYS);
            pstmt.setString(1, groupName);

            updated = pstmt.executeUpdate();
                    
            //Borramos los usuarios del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(DELETE_GROUP_USER_ALLUSERS);
            pstmt.setString(1, groupName);

            updated = pstmt.executeUpdate();
            
            //Buscamos y nos borramos si otros grupos nos tienen
            String LIST_GROUPS_LIST = "SELECT groupName, propValue FROM ofGroupProp WHERE "
                    + "name = 'sharedRoster.groupList'";
            
            pstmt = (PreparedStatement)connection.prepareStatement(LIST_GROUPS_LIST);
            rs = pstmt.executeQuery();
            
            ArrayList<String[]> gruposNosContienen = new ArrayList<String[]>();
            while(rs.next()) {
                String groupNameAc = rs.getString(1);
                String propValueAc = rs.getString(2);
                String []lstGrupos = propValueAc.split(",");
                for(String grupo:lstGrupos) {
                    if(grupo.equals(groupName)) {
                        gruposNosContienen.add(new String[]{groupNameAc, propValueAc});
                    }
                }
            }
            
            //Recorremos los grupos que nos tienen y nos quitamos
            for(String[] grupoNosContiene : gruposNosContienen) {
                String []lstGrupos=grupoNosContiene[1].split(",");
                String newPropValue = "";
                for(String grupo : lstGrupos) {
                    if(!grupo.equals(groupName)) {
                        newPropValue+=grupo + ",";
                    }
                }
                //Quitamos la última coma
                if(!newPropValue.isEmpty()) {
                        newPropValue = newPropValue.substring(0, newPropValue.length() - 1);
                }

                
                //Actualizamos el grupo
                String GROUP_UPDATED_PROPERTY = "UPDATE `ofGroupProp` SET `propValue`=?"
                + " WHERE `groupName` = ? AND `name` = 'sharedRoster.groupList'";
                pstmt = (PreparedStatement)connection.prepareStatement(GROUP_UPDATED_PROPERTY);
                pstmt.setString(1, newPropValue);
                pstmt.setString(2, grupoNosContiene[0]);
                
                pstmt.executeUpdate();
            }
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
    
    /**
     * Borra un usuario de un grupo de OpenFire
     * @param groupName Nombre del grupo de OpenFire
     * @param userName Nombre del usuario a borrar del grupo de OpenFire
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza la excepción
     * @throws XMPPExceptionSQL Si hay algún problema SQL se lanza la excepción
     * @throws UserNotFoundException Si no se encuentra el usuario se lanza la excepción
     */
    public void deleteGroupMember(String groupName, String userName)
            throws GroupNotFoundException, XMPPExceptionSQL, UserNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        UsersXMPP usersXMPP = new UsersXMPP();
        usersXMPP.setConnection(connection);
        
        if(usersXMPP.userExists(userName)) {
            throw new UserNotFoundException("El usuario no existe");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(DELETE_GROUP_USER);
            pstmt.setString(1, groupName);
            pstmt.setString(2, userName);            
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
    
    /**
     * Obtiene los miembros del grupo de OpenFire
     * @param groupName Nombre del grupo de OpenFire
     * @param isAdministrator Se indica si se quieren obtener los administradores o miembros normales
     * @return Devuelve un ArrayList con los nombres de los miembros
     * @throws XMPPExceptionSQL Si se produce algún error SQL se lanza la excepción
     * @throws GroupNotFoundException  Si no se encuentra el grupo se lanza la excepción
     */
    public ArrayList<String> getMembersGroup(String groupName, boolean isAdministrator) throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        ArrayList<String> members = new ArrayList<String>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            
            if (isAdministrator) {
                pstmt = (PreparedStatement)connection.prepareStatement(SELECT_GROUP_USER_USERNAMES);
                pstmt.setString(2, "1");
            }
            else {
                pstmt = (PreparedStatement)connection.prepareStatement(SELECT_GROUP_USER_USERNAMES);
                pstmt.setString(2, "0");
            }
            
            pstmt.setString(1, groupName);
            
            rs = pstmt.executeQuery();
            
            while (rs.next()) {
                String userName = rs.getString(1);
                if (userName != null) {
                    members.add(userName);
                }
            }
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }

            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return members;
    }
    
    /**
     * Obtiene los datos de un grupo de OpenFire
     * @param groupName Nombre del grupo de OpenFire
     * @return Devuelve un objeto GroupXMPP con todos los datos del grupo de OpenFire
     * @throws GroupNotFoundException Si el grupo no existe se lanza la excepción
     * @throws XMPPExceptionSQL Si se produce algún error SQL se lanza la excepción
     * @throws GroupCreateException Si se produce algún problema al crear los datos del grupo
     * se lanza la excepción
     */
    public GroupXMPP getGroup(String groupName) throws GroupNotFoundException, XMPPExceptionSQL, GroupCreateException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        String description = null;

        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {            
            pstmt = (PreparedStatement) connection.prepareStatement(SELECT_GROUP_DESCRIPTION);
            pstmt.setString(1, groupName);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                description = rs.getString(1);
            }
        }
        catch (SQLException e) {
            throw new XMPPExceptionSQL(e);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }
        }
        ArrayList<String> members = getMembersGroup(groupName, false);
        ArrayList<String> administrators = getMembersGroup(groupName, true);
        String displayName = getDisplayName(groupName);
        String showInRosteer = getShowInRoster(groupName);
        ArrayList<String> groupsShare = getGroupList(groupName);
        return new GroupXMPP(groupName, description, members, administrators, displayName, showInRosteer, groupsShare);
    }
    
    /**
     * Devuelve la visibilidad del grupo de OpenFire
     * @param groupName Nombre del grupo de OpenFire
     * @return Cadena con el tipo de visibilidad del grupo
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza la excepción
     */
    public String getShowInRoster(String groupName) throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        String showInRoster = "";
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = (PreparedStatement) connection.prepareStatement(SELECT_GROUP_PROPERTY);
            pstmt.setString(1, groupName);
            pstmt.setString(2, "sharedRoster.showInRoster");
            rs = pstmt.executeQuery();
            if (rs.next()) {
                showInRoster = rs.getString(1);
                
            }
        }
        catch (SQLException e) {
            throw new XMPPExceptionSQL(e);
        }
        finally {
            try {
                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return showInRoster;
    }
    
    /**
     * Devuelve el nombre con el que se muestra el grupo de OpenFire
     * @param groupName Nombre del grupo de OpenFire
     * @return Devuelve una cadena con el nombre con el que se muestra el grupo
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     * @throws GroupNotFoundException Si el grupo no existe se lanza la excepción
     */
    public String getDisplayName(String groupName) throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        String sharedName = "";
        try {
            
            pstmt = (PreparedStatement) connection.prepareStatement(SELECT_GROUP_PROPERTY);
            pstmt.setString(1, groupName);
            pstmt.setString(2, "sharedRoster.displayName");
            rs = pstmt.executeQuery();
            if (rs.next()) {
                sharedName = rs.getString(1);
            }
        }
        catch (SQLException e) {
            throw new XMPPExceptionSQL(e);
        }
        finally {
            try {
                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return sharedName;
    }
    
    /**
     * Devuelve la lista de grupos compartidos de un grupo
     * @param groupName Nombre del grupo de OpenFire
     * @return Devuelve un ArrayList con los nombres de los grupos compartidos
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza la excepción
     */
    public ArrayList<String> getGroupList(String groupName) throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        ArrayList<String> sharedGroups = new ArrayList<String>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;

        try {
            pstmt = (PreparedStatement) connection.prepareStatement(SELECT_GROUP_PROPERTY);
            pstmt.setString(1, groupName);
            pstmt.setString(2, "sharedRoster.groupList");
            rs = pstmt.executeQuery();
            if (rs.next()) {
                String listaGrupos = rs.getString(1);

                String[] grupos = listaGrupos.split(",");
                for(String grupo : grupos) {
                    sharedGroups.add(grupo);
                }
            }
        }
        catch (SQLException e) {
            throw new XMPPExceptionSQL(e);
        }
        finally {
            try {
                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return sharedGroups;
    }
    
    /**
     * Devuelve el número de grupos que tiene OpenFire
     * @return Devuelve el número de grupos que tiene OpenFire
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     */
    public int getGroupCount() throws XMPPExceptionSQL {
        int count = 0;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            
            pstmt = (PreparedStatement) connection.prepareStatement(SELECT_GROUP_COUNT);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                count = rs.getInt(1);
            }
        }
        catch (SQLException e) {
            throw new XMPPExceptionSQL(e);
        }
        finally {
            try {
                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return count;
    }
    
    /**
     * 
     * @return Devuelve los grupos que tienen grupos compartidos
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     */
    public ArrayList<String> getSharedGroupsNames() throws XMPPExceptionSQL {
        ArrayList<String> sharedGroups = new ArrayList<String>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = (PreparedStatement)connection.prepareStatement(SELECT_GROUP_PROPERTY_GROUPSHARED);
            rs = pstmt.executeQuery();
            
            while (rs.next()) {
                String userName = rs.getString(1);
                if (userName != null) {
                    sharedGroups.add(userName);
                }
            }
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {
                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return sharedGroups;
    }
    
    /**
     * Devuelve los nombres de los grupos que tiene OpenFire
     * @return Devuelve un ArrayList con los nombres de los grupos de OpenFire
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     */
    public ArrayList<String> getGroupNames() throws XMPPExceptionSQL {
        ArrayList<String> groupNames = new ArrayList<String>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = (PreparedStatement) connection.prepareStatement(SELECT_GROUP_NAME_NAMES);
            rs = pstmt.executeQuery();
            
            while (rs.next()) {
                groupNames.add(rs.getString(1));
            }
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {
                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return groupNames;
    }
    
    /**
     * Obtiene los grupos a los que pertenece el usuario
     * @param userName Nombre del usuario de OpenFire
     * @return Devuelve un ArrayList con los nombres de los grupos a los que pertenece el usuario
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     * @throws UserNotFoundException Si no se encuentra el usuario se lanza una excepción
     */
    public ArrayList<String> getGroupNames(String userName) throws XMPPExceptionSQL,
            UserNotFoundException{
        //TODO: Añadir en el SQL que saque aquellos grupos que son compartidos a todos los usuarios
        UsersXMPP usersXMPP = new UsersXMPP();
        usersXMPP.setConnection(connection);
        
        if(usersXMPP.userExists(userName)) {
            throw new UserNotFoundException("El usuario no existe");
        }
        
        ArrayList<String> groupNames = new ArrayList<String>();
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            pstmt = (PreparedStatement) connection.prepareStatement(SELECT_GROUP_USER_GROUPNAME);
            pstmt.setString(1, userName);
            rs = pstmt.executeQuery();
            
            while (rs.next()) {
                groupNames.add(rs.getString(1));
            }
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {
                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
        return groupNames;
    }
    
    /**
     * Establece a un grupo su lista de grupos a los que comparte
     * @param groupName Grupo de OpenFire al que se le establece la lista de grupos
     * @param groupList ArrayList con los nombres de los grupos a los que comparte el grupo
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza una excepción
     * @throws GroupModifyException Si no se puede modificar el grupo se lanza una excepción
     */
    private void setGroupList(String groupName, ArrayList<String> groupList)
            throws XMPPExceptionSQL, GroupNotFoundException, GroupModifyException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        if(!isEnableContactListGroupSharing(groupName)) {
            throw new GroupModifyException("El grupo no tiene una lista compartida");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            String groupListNew = "";
            for(String group : groupList) {
                groupListNew += group + ",";
            }
            if(!groupList.isEmpty()) {
                //Quitamos la última coma
                groupListNew = groupListNew.substring(0, groupListNew.length() - 1);
            }
            
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_PROPERTY_VALUE);
            pstmt.setString(1, groupName);
            pstmt.setString(2, "sharedRoster.groupList");
            pstmt.setString(3, groupListNew);
            
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
    
    /**
     * Añade un grupo a la lista de grupos que comparte el grupo de OpenFire
     * @param groupName Grupo de OpenFire al que se le añade el grupo
     * @param groupNameAdd Nombre del grupo que se va a añadir al grupo de OpenFire
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza una excepción
     * @throws GroupModifyException Si no se puede modificar el grupo se lanza una excepción
     */
    public void addGroupListGroup(String groupName, String groupNameAdd) throws XMPPExceptionSQL, GroupNotFoundException, GroupModifyException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo " + groupName + "no existe");
        }
        
        if(!existGroup(groupNameAdd)) {
            throw new GroupNotFoundException("El grupo " + groupNameAdd + "no existe");
        }
        
        if(!isEnableContactListGroupSharing(groupName)) {
            throw new GroupModifyException("El grupo no tiene una lista compartida");
        }
        
        //Si el grupo contiene al grupo a borarr en su lista de grupos
        if(!isGroupContentGroupList(groupName, groupNameAdd)) {
            ArrayList<String> groupList = getGroupList(groupName);
            groupList.add(groupNameAdd);
            
            setGroupList(groupName, groupList);
        }
    }
    
    /**
     * Borra un grupo de la lista de grupos que comparte el grupo de OpenFire
     * @param groupName Grupo de OpenFire al que se le borra el gruo
     * @param groupNameDel Nombre del grupo que se va a eliminar del grupo de OpenFire
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza una excepción
     * @throws GroupModifyException Si no se puede modificar el grupo se lanza una excepción
     */
    public void delGroupListGroup(String groupName, String groupNameDel) throws XMPPExceptionSQL, GroupNotFoundException, GroupModifyException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        if(!isEnableContactListGroupSharing(groupName)) {
            throw new GroupModifyException("El grupo no tiene una lista compartida");
        }
        
        //Si el grupo contiene al grupo a borarr en su lista de grupos
        if(isGroupContentGroupList(groupName, groupNameDel)) {
            ArrayList<String> groupList = getGroupList(groupName);
            groupList.remove(groupNameDel);
            
            setGroupList(groupName, groupList);
        }
    }
    
    /**
     * Comprueba si un grupo está dentro de la lista de grupos compartidos de otro
     * @param groupName Grupo de OpenFire al que se le comprueba si está el grupo
     * @param groupNameSearch Grupo a comprobar en la lista del otro grupo de OpenFire
     * @return Devuelve un valor booleano que indica si está o no en la lista
     * @throws XMPPExceptionSQL
     * @throws GroupNotFoundException 
     */
    public boolean isGroupContentGroupList(String groupName, String groupNameSearch) throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        if(!existGroup(groupNameSearch)) {
            throw new GroupNotFoundException("El grupo a buscar existe");
        }
        
        ArrayList<String> groupList = getGroupList(groupName);
        
        return groupList.contains(groupNameSearch);
    }
    
    /**
     * Habilita la lista de grupos compartidos del grupo de OpenFire
     * @param groupName Grupo de OpenFire al que se le habilita
     * @param groupSharing Nombre del grupo compartido que les aparecerá a los
     * usuarios del grupo
     * @throws GroupNotFoundException Si no se encuentra el grupo se lanza una excepción
     * @throws XMPPExceptionSQL Si se produce un error SQL se lanza la excepción
     */
    public void enableContactListGroupSharing(String groupName, String groupSharing) throws GroupNotFoundException, XMPPExceptionSQL {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        updateGroupSharedDisplayName(groupName, groupSharing);
        
        
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_PROPERTY_VALUE);
            pstmt.setString(1, "onlyGroup");
            pstmt.setString(2, groupName);
            pstmt.setString(3, "sharedRoster.showInRoster");
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
            
    public void disableContactListGroupSharing(String groupName) throws GroupNotFoundException, XMPPExceptionSQL {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        updateGroupSharedDisplayName(groupName, "");
        
        
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_PROPERTY_VALUE);
            pstmt.setString(1, "nobody");
            pstmt.setString(2, groupName);
            pstmt.setString(3, "sharedRoster.showInRoster");
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }

    public boolean isEnableContactListGroupSharing(String groupName) throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }        
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(SELECT_GROUP_PROPERTY);
            pstmt.setString(1, groupName);
            pstmt.setString(2, "sharedRoster.showInRoster");
            rs = pstmt.executeQuery();
            
            if(rs.next()) {
                if(rs.getString(1).equals("nobody")) {
                    return false;
                } else {
                    return true;
                }
            } else {
                return false;
            }
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
        
    public void updateGroupMember(String groupName, String userName,
            boolean isAdministratorNew) throws GroupNotFoundException, XMPPExceptionSQL, UserNotFoundException, GroupModifyException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        UsersXMPP usersXMPP = new UsersXMPP();
        usersXMPP.setConnection(connection);
        
        if(usersXMPP.userExists(userName)) {
            throw new UserNotFoundException("El usuario no existe");
        }
        
        //Comprobamos si el usuario pertenece al grupo
        if(!getGroupNames(userName).contains(groupName)) {
            throw new GroupModifyException("El usuario no pertenece al grupo");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_MEMBER_ADMINISTRATOR);
            if(isAdministratorNew) {
                pstmt.setString(1, "1");
            } else {
                pstmt.setString(1, "0");
            }
            pstmt.setString(2, groupName);
            pstmt.setString(3, userName);            
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
    
    public void updateGroupShowInRoster(String groupName, String showInRosterNew)
            throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_PROPERTY_VALUE);
            pstmt.setString(1, groupName);
            pstmt.setString(2, "sharedRoster.showInRoster");
            pstmt.setString(3, showInRosterNew);
            
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
     
    private void updateGroupSharedDisplayName(String groupName, String displayNameNew)
            throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_PROPERTY_VALUE);
            pstmt.setString(1, displayNameNew);
            pstmt.setString(2, groupName);
            pstmt.setString(3, "sharedRoster.displayName");
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
    
    public void updateGroupName(String groupName, String groupNameNew)
            throws XMPPExceptionSQL, GroupNotFoundException {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos el nombre del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPADTE_GROUP_NAME);
            pstmt.setString(1, groupNameNew);
            pstmt.setString(2, groupName);
            
            pstmt.executeUpdate();
            
            //Actualizamos las propiedades del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_PROPERTY_NAME);
            pstmt.setString(1, groupNameNew);
            pstmt.setString(2, groupName);
            
            pstmt.executeUpdate();
            
            //Actualizamos los miembros de los grupos
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_USER_NAME);
            pstmt.setString(1, groupNameNew);
            pstmt.setString(2, groupName);
            
            pstmt.executeUpdate();
            
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
    
    public void updateGroupDescription(String groupName, String groupDescriptionNew)
            throws GroupNotFoundException, XMPPExceptionSQL {
        if(!existGroup(groupName)) {
            throw new GroupNotFoundException("El grupo no existe");
        }
        
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //Actualizamos la descripción del grupo
            pstmt = (PreparedStatement)connection.prepareStatement(UPDATE_GROUP_DESCRIPTION);
            pstmt.setString(1, groupDescriptionNew);
            pstmt.setString(2, groupName);
            
            pstmt.executeUpdate();
        }
        catch (SQLException sqle) {
            throw new XMPPExceptionSQL(sqle);
        }
        finally {
            try {

                if(pstmt != null) {
                    pstmt.close();
                }

                if(rs != null) {
                    rs.close();
                }
                
            } catch (SQLException sqle) {
                throw new XMPPExceptionSQL(sqle);
            }            
        }
    }
}
